package com.limanyik.family.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.limanyik.family.common.enums.ReqChannelEnum;
import com.limanyik.family.common.utils.FamilyHttpRequest;
import com.limanyik.family.common.utils.InteriorEncryptUtil;
import com.limanyik.family.common.utils.IpUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Map;

public class AuthenticateFilter implements GlobalFilter, GatewayFilter, Ordered {

    /**
     * 鉴定请求是否正确
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取HTTP请求信息 permissionFilter
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        // 请求方式(POST/GET/DELETE/PUT)
        String method = serverHttpRequest.getMethodValue();
        // 请求IP地址
        String requestIp = IpUtils.getRealIpAddress(serverHttpRequest);
        // 媒体类型
        MediaType mediaType = serverHttpRequest.getHeaders().getContentType();
        // 处理POST请求,解析参数
        if (HttpMethod.POST.name().equalsIgnoreCase(method) && (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON_UTF8.isCompatibleWith(mediaType))) {
            ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
            Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
                    .flatMap(body -> {
                        try {
                            JSONObject jsonObject = null;
                            jsonObject = JSON.parseObject(body);
                            String publicKey = jsonObject.getString("secretKey");
                            String sign = jsonObject.getString("sign");
                            ReqChannelEnum reqFrom = null;
                            if (StringUtils.isNotBlank(jsonObject.getString("reqChannel"))) {
                                reqFrom = ReqChannelEnum.valueOf(jsonObject.getString("reqChannel"));
                            }
                            Long timestamp = jsonObject.getLong("requestTimeSpear");
                            String token = jsonObject.getString("token");
                            String version = jsonObject.getString("version");
                            JSONObject reqVO = jsonObject.getJSONObject("data");
                            FamilyHttpRequest familyHttpRequest = new FamilyHttpRequest();
                            familyHttpRequest.setRequestTimeSpear(timestamp);
                            familyHttpRequest.setSecretKey(publicKey);
                            familyHttpRequest.setToken(token);
                            familyHttpRequest.setReqChannel(reqFrom);
                            familyHttpRequest.setIp(requestIp);
                            familyHttpRequest.setSign(sign);
                            familyHttpRequest.setData(reqVO);
                            familyHttpRequest.setVersion((version = StringUtils.isBlank(version) ? "1.0.0" : version));
                            jsonObject.put("ip", requestIp);
                            body = JSONObject.toJSONString(jsonObject);
                            exchange.getAttributes().put("familyHttpRequest", familyHttpRequest);
                            return Mono.just(body);
                        } catch (Exception e) {
                            return Mono.error(new Exception("参数不匹配"));
                        }
                    });
            // system interface
            BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
            HttpHeaders headers = new HttpHeaders();
            headers.putAll(exchange.getRequest().getHeaders());
            headers.remove(HttpHeaders.CONTENT_LENGTH);
            MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headers);
            return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
                ServerHttpRequest decorator = this.decorate(exchange, headers, outputMessage);
                return chain.filter(exchange.mutate().request(decorator).build());
            }));
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -30;
    }

    /**
     * 重写ModifyRequestBodyGatewayFilterFactory的方法
     *
     * @Author rqwang
     * @Date 2020/11/2
     */
    ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, MyCachedBodyOutputMessage outputMessage) {
        return new ServerHttpRequestDecorator(exchange.getRequest()) {
            public HttpHeaders getHeaders() {
                long contentLength = headers.getContentLength();
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                if (contentLength > 0L) {
                    httpHeaders.setContentLength(contentLength);
                } else {
                    httpHeaders.set("Transfer-Encoding", "chunked");
                }
                return httpHeaders;
            }

            public Flux<DataBuffer> getBody() {
                return outputMessage.getBody();
            }
        };
    }

}
